package com.mundus.world.observer;

import org.lwjgl.util.vector.Matrix4f;
import org.lwjgl.util.vector.Vector3f;
import org.lwjgl.util.vector.Vector4f;

import com.mundus.engine.Settings;
import com.mundus.engine.assets.MathHelper;

public class Observer {	
	private Matrix4f _view;
	private Matrix4f _projection;
	
	private Vector3f _camPos;
	private Vector4f _camRot;
	private Vector3f _camTarget;
	private Vector3f _camUp;
	
	private float _far = 512f;
	private float _near = 0.1f;
	private float _fov;
	
	public Matrix4f getView() { return _view;}
	public Matrix4f getProjection() { return _projection;}
	public Matrix4f getViewProjection() { return Matrix4f.mul(_view, _projection, null);}
	
	public Vector3f getPosition(){ return _camPos;}
	public Vector4f getRotation(){ return _camRot;}
	public Vector3f getTarget(){ return _camTarget;}
	public Vector3f getUp(){ return _camUp;}
	
	public float getFarPlane(){ return _far;}
	public float getNearPlane(){ return _near;}
	public float getFieldOfView(){ return _fov;}
	
	public Observer (Vector3f position, Vector4f rotation, float fieldOfView){		
		this._camPos = position;
		this._camRot = rotation;
		this._fov = fieldOfView;
		
		setupView();
		setupProjection_perspective();
	}
	
	public void update(){
		setupView();
	}

	private void setupView(){
		_view = new Matrix4f();		
	    Matrix4f.setIdentity(_view);
	    
	    Vector3f xAxis = new Vector3f(); //Right Vector
	    Vector3f yAxis = new Vector3f(0,1,0); //Up Vector
	    Vector3f zAxis = new Vector3f(0,0,-1); //Forward Vector	    
	   
	    Vector3f.cross(zAxis, yAxis, xAxis);
	    Vector3f.cross(xAxis, zAxis, yAxis);
	    zAxis.negate(zAxis);

	    _view.m00 = xAxis.x; _view.m10 = xAxis.y; _view.m20 = xAxis.z;
	    _view.m01 = yAxis.x; _view.m11 = yAxis.y; _view.m21 = yAxis.z;
	    _view.m02 = zAxis.x; _view.m12 = zAxis.y; _view.m22 = zAxis.z;
	    _view.m33 = 1;

	    _view.translate(_camPos);
	    _view.rotate(MathHelper.degreesToRadians(_camRot.w), new Vector3f(_camRot.x, _camRot.y, _camRot.z));
		_view.invert();
		
		this._camUp = yAxis;
		this._camTarget = zAxis;
	}
	
	private void setupProjection_perspective(){		
		_projection = new Matrix4f();
		float y_scale = (float)(1/Math.tan(MathHelper.degreesToRadians(_fov/2f)));
		float x_scale = y_scale/Settings.getAspectRatio();
		float frustum_length = _far - _near;
		
		_projection.m00 = x_scale;
		_projection.m11 = y_scale;
		_projection.m22 = -((_far + _near) / frustum_length);
		_projection.m23 = -1;
		_projection.m32 = -((2 * _near * _far) / frustum_length);
		_projection.m33 = 0;
	}
	
	private void setupProjection_orthographic(){
		_projection = new Matrix4f();		
		
		float top = 0;
		float bottom = Settings.getResolution().getHeight();
		float left = 0;
		float right = Settings.getResolution().getWidth();
		float far = _far;
		float near = _near;
		
		_projection.m00 = 2.0f/(right - left);
		_projection.m11 = 2.0f /(top - bottom);
		_projection.m22 = 2.0f /(far - near);
		_projection.m30 = -(right+left)/(right-left);
		_projection.m31 = -(top+bottom)/(top-bottom);
		_projection.m32 = -(far + near)/(far - near);
		_projection.m33 = 1;
	}
}
